import org.lwjgl.*;
import org.lwjgl.opengl.*;
import org.lwjgl.input.*;

import java.io.IOException;
import java.nio.*;
public class BaseWindow 
{

  protected static boolean isRunning = false;
  static int width;
  static int height;
  static boolean fullscreen = false;
  static DisplayMode[] dm;
  
  public static void main(String[] args) {
    (new BaseWindow()).execute();
  }  

	/**
	 * Initializes display and enters main loop
	 */
	protected void execute()
	{
		try {
			initDisplay();
		} catch (LWJGLException e) {
			System.err.println("Can't open display.");
			System.exit(0);
		}		
		
		BaseWindow.isRunning = true;
		mainLoop();
		Display.destroy();
	}
	
	
	/**
	 * Main loop: renders and processes input events
	 */
	protected void mainLoop()
	{
	  // setup camera and lights
	  setupView();
	  
	  while (BaseWindow.isRunning) {
	    //reset view
	  resetView();

	    // let subsystem paint
      renderFrame();
      
      // process input events
      processInput();

      // update window contents and process input messages
      Display.update();
	  }
	}

	/**
	 * Initial setup of projection of the scene onto screen, lights, etc.
	 */
	protected void setupView()
	{
	  
	}
	
	/**
	 * Resets the view of current frame
	 */
	protected void resetView()
	{
	  
	}
	
  /**
   * Renders current frame
   */
  protected void renderFrame()
  {
    
  }

  /**
   * Processes Keyboard and Mouse input and spawns actions
   */
  protected void processInput()
  {
    if (Display.isCloseRequested() || Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) 
    {
      BaseWindow.isRunning = false;
    }
    if(Keyboard.isKeyDown(Keyboard.KEY_F3) && !fullscreen){
    	fullscreen = true;
    	try {
    		Display.setFullscreen(true);
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
    }
    if(Keyboard.isKeyDown(Keyboard.KEY_F4) && fullscreen){
    	fullscreen = false;
    	try {
    		Display.setFullscreen(false);
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
    }
  }
  
	/**
	 * Finds best 1024x768 display mode and sets it
	 * @throws LWJGLException
	 */
	protected void initDisplay() throws LWJGLException
	{

 		dm = Display.getAvailableDisplayModes();

 		
		Display.setDisplayMode(dm[1]);
		width  = dm[1].getWidth();
		height = dm[1].getHeight();
		Display.create(); 		
		Display.setTitle(this.getClass().getName());
		// Enable vsync if we can (due to how OpenGL works, it cannot be guarenteed to always work)
		System.out.println("GL RENDERER: " + GL11.glGetString(GL11.GL_RENDERER));
		System.out.println("GL VENDOR: " + GL11.glGetString(GL11.GL_VENDOR));
		System.out.println("GL VERSION: " + GL11.glGetString(GL11.GL_VERSION));

		ContextCapabilities caps = GLContext.getCapabilities();

		System.out.println("OpenGL 3.0: " + caps.OpenGL30);
		System.out.println("OpenGL 3.1: " + caps.OpenGL31);
		System.out.println("OpenGL 3.2: " + caps.OpenGL32);
		System.out.println("ARB_compatibility: " + caps.GL_ARB_compatibility);
		
	}
	
	public static void closeGame(){
		
		System.exit(0);
	}

  /**
   * Utils for creating native buffers
   * @throws LWJGLException
   */
  public static ByteBuffer allocBytes(int howmany) {
    return ByteBuffer.allocateDirect(howmany).order(ByteOrder.nativeOrder());
  }

  public static IntBuffer allocInts(int howmany) {
    return ByteBuffer.allocateDirect(howmany).order(ByteOrder.nativeOrder()).asIntBuffer();
  }

  public static FloatBuffer allocFloats(int howmany) {
    return ByteBuffer.allocateDirect(howmany).order(ByteOrder.nativeOrder()).asFloatBuffer();
  }

  public static ByteBuffer allocBytes(byte[] bytearray) {
    ByteBuffer bb = ByteBuffer.allocateDirect(bytearray.length * 1).order(ByteOrder.nativeOrder());
    bb.put(bytearray).flip();
    return bb;
  }

  public static IntBuffer allocInts(int[] intarray) {
    IntBuffer ib = ByteBuffer.allocateDirect(intarray.length * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
    ib.put(intarray).flip();
    return ib;
  }

  public static FloatBuffer allocFloats(float[] floatarray) {
    FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    fb.put(floatarray).flip();
    return fb;
  }
}
